﻿using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;

namespace NUnit4PowerShell
{
    static public class Utils
    {
        static public string GetFilePath(string fileName)
        {
            if (fileName == null)
            {
                return null;
            }
            string fullPath;
            if (!File.Exists(fileName))
            {
                // If the file doesn't exist with check specificly inside the folder defined above
                fullPath = System.IO.Path.Combine(Utils.GetFolderPath(), fileName);
                if (!File.Exists(fullPath))
                {
                    throw new FileNotFoundException(
                        string.Format(
                            "File containing tests to execute can not be found at the path indicated in the key '{0}' in {1}",
                            fileName,
                            AppDomain.CurrentDomain.SetupInformation.ConfigurationFile));
                }
            }
            else
            {
                fullPath = System.IO.Path.GetFullPath(fileName);
            }
            return fullPath;
        }

        static public string GetDllConfigFileName()
        {
            return "NUnit4PowerShell.config";// "NUnit4PowerShell.config";
        }

        static public string GetFolderPath()
        {
            Uri u = new Uri(Assembly.GetExecutingAssembly().CodeBase);
            string dir = System.IO.Path.GetDirectoryName(u.LocalPath);

            ConfigurationFileMap configurationFileMap =
                new ConfigurationFileMap(string.Format(@"{0}\{1}", dir, GetDllConfigFileName()));
            //Configuration config = ConfigurationManager.OpenMappedMachineConfiguration(configurationFileMap);
            Configuration config = ConfigurationManager.OpenMappedMachineConfiguration(configurationFileMap);
            //AppSettingsSection appSettingSection = (AppSettingsSection)config.GetSection("appSettings");


            // Folder path containing the file to tests -> used for ls in PowerShell
            string folderPath = config.AppSettings.Settings["FolderPath"].Value;//ConfigurationManager.AppSettings["FolderPath"];
            //appSettingSection.Settings["FolderPath"].Value;//config.AppSettings.Settings["FolderPath"].Value;
            if (string.IsNullOrEmpty(folderPath))
            {
                // If field is not filled : we use dll current directory
                folderPath = dir;
            }

            if (!Directory.Exists(folderPath))
            {
                throw new FileNotFoundException(
                    string.Format("Folder containing the test file to execute can not be found at the path indicated in the key 'TestFileFolderPath' in {0}",
                                  AppDomain.CurrentDomain.SetupInformation.ConfigurationFile));
            }
            folderPath = System.IO.Path.GetFullPath(folderPath);

            return folderPath;
        }

        static public string GetPSFixtureNamePattern()
        {
            Uri u = new Uri(Assembly.GetExecutingAssembly().CodeBase);
            string dir = System.IO.Path.GetDirectoryName(u.LocalPath);

            ConfigurationFileMap configurationFileMap = new ConfigurationFileMap(string.Format(@"{0}\{1}", dir, GetDllConfigFileName()));
            Configuration config = ConfigurationManager.OpenMappedMachineConfiguration(configurationFileMap);
            string namePattern = config.AppSettings.Settings["PSFixtureNamePattern"].Value;

            if (string.IsNullOrEmpty(namePattern))
            {
                throw new ArgumentException(
                    string.Format("The setting 'PSFixtureNamePattern' in the configuration file has not been filled. Check {0}",
                                  AppDomain.CurrentDomain.SetupInformation.ConfigurationFile));
            }

            return namePattern;
        }

        static public string GetMockCmdLetScriptPath()
        {
            return GetFileFullPath("CmdLetMocksScript", false);
        }

        static public string GetPSTestFunctionNamePattern()
        {
            Uri u = new Uri(Assembly.GetExecutingAssembly().CodeBase);
            string dir = System.IO.Path.GetDirectoryName(u.LocalPath);

            ConfigurationFileMap configurationFileMap = new ConfigurationFileMap(string.Format(@"{0}\{1}", dir, GetDllConfigFileName()));
            Configuration config = ConfigurationManager.OpenMappedMachineConfiguration(configurationFileMap);
            string namePattern = config.AppSettings.Settings["PSTestFunctionNamePattern"].Value;
            //string namePattern = ConfigurationManager.AppSettings["TestFunctionNamePattern"];
            if (string.IsNullOrEmpty(namePattern))
            {
                throw new ArgumentException(
                    string.Format("The setting 'TestFunctionNamePattern' in the configuration file has not been filled. Check {0}",
                                  AppDomain.CurrentDomain.SetupInformation.ConfigurationFile));
            }

            return namePattern;
        }

        static private string GetFileFullPath(string settingString, bool isMandatoryField)
        {
            Uri u = new Uri(Assembly.GetExecutingAssembly().CodeBase);
            string dir = System.IO.Path.GetDirectoryName(u.LocalPath);

            ConfigurationFileMap configurationFileMap = new ConfigurationFileMap(string.Format(@"{0}\{1}", dir, GetDllConfigFileName()));
            Configuration config = ConfigurationManager.OpenMappedMachineConfiguration(configurationFileMap);
            
            string fileNameInSetting = config.AppSettings.Settings[settingString].Value;

            string fullPath = null;
            // If the field is not filled then we return null
            if (fileNameInSetting == null || fileNameInSetting.Trim() == string.Empty)
            {
                if (isMandatoryField)
                {
                    throw new ArgumentException(
                        string.Format("The setting '{0}' in the configuration file has not been filled. Check the file {1}",
                        settingString,
                        AppDomain.CurrentDomain.SetupInformation.ConfigurationFile));
                }
                return fullPath;
            }

            // If mock file name is filled, we have to check the file exists
            if (!File.Exists(fileNameInSetting))
            {
                // If the file doesn't exist with check specificly inside the folder defined above
                fullPath = System.IO.Path.Combine(GetFolderPath(), fileNameInSetting);
                if (!File.Exists(fullPath))
                {
                    throw new FileNotFoundException(
                        string.Format(
                            "File containing tests to execute can not be found at the path indicated in the key '{0}' in {1}",
                            settingString,
                            AppDomain.CurrentDomain.SetupInformation.ConfigurationFile));
                }
            }
            else
            {
                fullPath = System.IO.Path.GetFullPath(fileNameInSetting);
            }

            return fullPath;
        }

        static public string[] GetPSFixtureFiles()
        {
            var folder = Utils.GetFolderPath();
            string[] files = Directory.GetFiles(folder, Utils.GetPSFixtureNamePattern(),
                                                SearchOption.TopDirectoryOnly);


            if (files.Length == 0)
            {
                throw new FileNotFoundException(string.Format("None fixture has been found to be executed in '{0}'",
                                                              folder));
            }

            // We inverse the list to have the fixture execute in the right direction
            //// Note : I could use linq with : arr.Reverse<string> but I will have to use .NET 3.5 framework
            //string[] fixtures = new string[files.Length];
            //int j = 0;
            //for (int i = files.Length - 1; i >= 0; i--)
            //{
            //    fixtures[j] = files[i];
            //    j++;
            //}

            //return fixtures;
            return files;
        }
    }

    public class Logger
    {
        public const string LogFileName = "NUnit4PowerShell.log";
        private const string DatePattern = "yyyy-MM-dd HH:mm:ss,ms";
        private string logPath = "";
        private Logger()
        {
            Uri u = new Uri(Assembly.GetExecutingAssembly().CodeBase);
            string dir = System.IO.Path.GetDirectoryName(u.LocalPath);

            logPath = System.IO.Path.Combine(dir, LogFileName);
            if (File.Exists(logPath))
            {
                File.Delete(logPath);
            }
            WriteLog("Logger initialization");
        }

        public void Info(string msg)
        {
            msg = DateTime.Now.ToString(DatePattern) + " [INFO]  " + msg;
            WriteLog(msg);
        }

        public void AddEmptyLine()
        {
            WriteLog("");
        }

        public void Info(string msg, string source)
        {
            msg = DateTime.Now.ToString(DatePattern) + " [" + source + "]" + " [INFO]  " + msg;
            WriteLog(msg);
        }

        public void Error(string msg)
        {
            msg = DateTime.Now.ToString(DatePattern) + " [ERROR] " + msg;
            WriteLog(msg);
        }

        public void Error(string msg, string source)
        {
            msg = DateTime.Now.ToString(DatePattern) + " [" + source + "]" + " [ERROR] " + msg;
            WriteLog(msg);
        }

        private void WriteLog(string msg)
        {
            File.AppendAllText(logPath, msg+Environment.NewLine, Encoding.UTF8);
        }

        private static Logger instance;

        public static Logger Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new Logger();
                }
                return instance;
            }
        }
    }
}
